package com.sykj.seaflow.warm.core.utils;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.ListUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.sykj.seaflow.warm.core.listener.GlobalAssignmentListener;
import com.sykj.seaflow.warm.core.listener.GlobalCreateListener;
import com.sykj.seaflow.warm.module.def.bean.SeaFlowDef;
import com.sykj.seaflow.warm.module.def.bean.SeaFlowNode;
import com.sykj.seaflow.warm.module.design.entity.SeaFlowDesign;
import org.dromara.warm.flow.core.entity.Definition;
import org.dromara.warm.flow.core.entity.Node;
import org.dromara.warm.flow.core.entity.Skip;
import org.dromara.warm.flow.core.enums.NodeType;
import org.dromara.warm.flow.core.enums.SkipType;
import org.dromara.warm.flow.core.listener.Listener;
import org.dromara.warm.flow.core.utils.FlowConfigUtil;
import org.dromara.warm.flow.orm.entity.FlowDefinition;
import org.dromara.warm.flow.orm.entity.FlowNode;
import org.dromara.warm.flow.orm.entity.FlowSkip;
import org.dom4j.Document;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;

public class FlowUtils {
    /**
     * xml 转 json
     * @param xml
     * @return
     */
    public static   String xmlToJsonStr(String xml){
//        Document
        return null;
    }

    public static void main(String[] args) {
        Definition definition = new FlowDefinition();
        definition.setFlowCode("test");
        definition.setFlowName("测试");
        definition.setFormCustom("N");
        definition.setId(9999L);
        definition.setVersion("1");
        definition.setFormPath("system/leave/approve");
        definition.setIsPublish(0);
        //        节点类型（0开始节点 1中间节点 2结束节点 3互斥网关 4并行网关）
        String flowJson  = "[{\"nodeId\":\"e44a5f8c-655f-4eec-9a55-ecd71a186942\",\"nodeType\":\"start\",\"nodeName\":\"开始节点\",\"value\":\"所有人\"},{\"nodeId\":\"f9a3d232-fcdc-4347-9a3c-cc53e91fcc49\",\"nodeType\":\"serial\",\"nodeName\":\"分支选择\",\"childNodes\":[[{\"nodeId\":\"fe4c6fb0-d98c-4142-8a8e-e7a8e192dfb0\",\"nodeType\":\"serial-node\",\"nodeName\":\"条件\",\"value\":\"请设置条件\"},{\"nodeId\":\"6aeb2579-21b1-498e-8e89-448cdcb95f3f\",\"nodeName\":\"审批人\",\"nodeType\":\"between\",\"value\":\"\"}],[{\"nodeId\":\"4eeebcee-3be9-4e27-81c9-b186bd77a8e0\",\"nodeType\":\"serial-node\",\"nodeName\":\"条件\",\"value\":\"\"}],[{\"nodeId\":\"1db1bc73-6499-4629-b2d2-eff5feda376c\",\"nodeType\":\"serial-node\",\"nodeName\":\"条件\",\"value\":\"其他条件走此流程\",\"enableDel\":false},{\"nodeId\":\"1474738e-081c-4f99-9b88-37fa5ab2a141\",\"nodeName\":\"审批人\",\"nodeType\":\"between\",\"value\":\"\"}]]},{\"nodeId\":\"fa74b35d-a79c-4323-8213-28ac4b4eb303\",\"nodeType\":\"parallel\",\"nodeName\":\"并行分支\",\"childNodes\":[[{\"nodeId\":\"1b7504c1-b459-456f-8c3e-43bc7a8b1cf8\",\"nodeName\":\"审批人1\",\"nodeType\":\"parallel-node\",\"value\":\"\"}],[{\"nodeId\":\"8d6f6f4b-080b-4069-a199-1b811ca0cda9\",\"nodeName\":\"审批人2\",\"nodeType\":\"parallel-node\",\"value\":\"\"}]]},{\"nodeId\":\"e292a75b-9f10-4b36-888a-8374ac45a291\",\"nodeType\":\"end\"}]";
        JSONArray jsonArray = JSONUtil.parseArray(flowJson);
        String endNodeCode = jsonArray.getJSONObject(jsonArray.size() - 1).getStr("nodeId");
        List<Node> nodeList = new ArrayList<>();
        convertNode(nodeList, null, jsonArray, endNodeCode);
        //nodeList  倒叙排序
        List<Node> nodeList1 = ListUtil.reverse(nodeList);

        definition.setNodeList(nodeList1);
        Document document = FlowConfigUtil.createDocument(definition);
        System.out.println(document.asXML());
    }

    public static String getNextVersion(String version) {
        if(ObjectUtil.isEmpty(version)){
            return "1.0";
        }
        float v = Float.parseFloat(version);
        v =  v + 1;
        return  Float.toString(v);

    }


    public static SeaFlowDef convertFlow(SeaFlowDesign seaflowDesign){
        //获取所有，
        JSONArray jsonArray = JSONUtil.parseArray(seaflowDesign.getFlowJson());
        SeaFlowDef flowDefinition = BeanUtil.copyProperties(seaflowDesign, SeaFlowDef.class);
        flowDefinition.setVersion(seaflowDesign.getFlowVersion());
        List<Node> nodeList = new ArrayList<>();
        String endNodeCode = jsonArray.getJSONObject(jsonArray.size() - 1).getStr("nodeId");
        convertNode(nodeList, null, jsonArray, endNodeCode);
        //nodeList  倒叙排序
        List<Node> nodeList1 = ListUtil.reverse(nodeList);
        flowDefinition.setNodeList(nodeList1);
//        Document document = FlowConfigUtil.createDocument(flowDefinition);
//
//        return document.asXML();
        return flowDefinition;
//
    }




    /**
     *
     * @param nodeList
     * @param skipNodeCode 表示要跳转的节点， 可能是多个 用逗号分割吧
     * @param jsonArray
     * @param endNodeCode  结束节点node
     * @return
     */
    private static String convertNode(List<Node> nodeList, String skipNodeCode, JSONArray jsonArray, String endNodeCode){

        // 从后向前推断,  结束-> 开始
        for (int i = jsonArray.size() -1; i >= 0; i--) {
            List<Skip> skipList = new ArrayList<>();
            // 单个对象
            JSONObject jsonObject = jsonArray.getJSONObject(i);
            // 节点类型
            String nodeType = jsonObject.getStr("nodeType");

            // 分支节点 直接返回 上一个code;  处理分支的连线问题，  serial-node为分支条件， 无需加入node中
            if(ObjectUtil.equal(nodeType, "serial-node")){
                return skipNodeCode;
            }
            //并行网关时， 子节点最后一个连接到网关上
            boolean isParallel = ObjectUtil.equal(nodeType, "parallel");
            // 记录并行网关的每一个子节点的第一个nodeCode， 给网关上一个节点连接
            List<String> parallelFirstNodes = new ArrayList<>();
            // 获取子节点
            JSONArray childNodes = jsonObject.getJSONArray("childNodes");

            FlowNode node = convertToNode(jsonObject);

            //如果有子节点 先处理子节点 ，并处理节点的连线问题

            FlowNode parallelCopy = null;

            if(ObjectUtil.isNotEmpty(childNodes)){


                // 如果是条件分支， 只要按顺序连接就行，
                // 如果是并行分支，parallel 直接连接后一个节点，子节点的最后一个连接当前节点， 自己点的第一个和 parallel的前一个节点连接
                if(isParallel){
                    // 拷贝一个网关， 前后都设置一个网关吧
                    parallelCopy = BeanUtil.copyProperties(node, FlowNode.class);
                    parallelCopy.setNodeCode(UUID.randomUUID().toString());

                        //  如果是并行网关，  并行网关连接后一个节点， 并且 并行网关所有子节点的最后一个连接到并行网关
                        Skip skip = new FlowSkip();
                        skip.setNowNodeCode(node.getNodeCode());
                        skip.setSkipType(SkipType.PASS.getKey());
                        skip.setNextNodeCode(skipNodeCode);
                        skipList.add(skip);


                    // 更改跳转节点， 让子节点跳转到网关上
                    skipNodeCode = node.getNodeCode();

                }

                // 子节点是2层数组， 所以要先循环一次
                for (int j = 0; j < childNodes.size(); j++) {

                    JSONArray subArray = childNodes.getJSONArray(j);
                    // 这里获子节点的第2个(不存在就返回父节点的后面一个节点）
                    String lastNodeCode =   convertNode(nodeList, skipNodeCode, subArray, endNodeCode);
                    // 返回每个子节点中的第一个code
                    if(isParallel){
                        parallelFirstNodes.add(lastNodeCode);


                    }else{
                        //  条件分支才走这里了
                        // 每个子节点的 第一个节点
                        JSONObject subNode = subArray.getJSONObject(0);
                        //default ：默认跳转配置， 只有条件分支才有
                        Boolean defaultSerial = subNode.getBool("default");
                        if(defaultSerial != null && defaultSerial){
                            Skip skip = new FlowSkip();
                            // 设置表单时类别 简单模式 或 复杂模式
                            String expressionType = "@@default@@|true";
                            skip.setSkipCondition( expressionType );
                            skip.setNowNodeCode(node.getNodeCode());
                            skip.setSkipName(subNode.getStr("nodeName"));
                            skip.setSkipType(SkipType.PASS.getKey());
                            skip.setNextNodeCode(lastNodeCode);
                            skipList.add(skip);
                        }else{
                            //设置跳转
                            JSONObject config = subNode.getJSONObject("config");
                            JSONObject conditions = config.getJSONObject("conditions");
                            // 只有条件分支才有conditions
                            if(conditions != null){
                                Boolean simple = conditions.getBool("simple");
                                Boolean group = conditions.getBool("group");
                                String simpleData = conditions.getStr("simpleData");
                                Skip skip = new FlowSkip();
                                // 设置表单时类别 简单模式 或 复杂模式
                                String expressionType = "@@" +  (simple? "simple_"+ group : "complex") + "@@|";
                                skip.setSkipCondition( expressionType +  simpleData );
                                skip.setNowNodeCode(node.getNodeCode());
                                skip.setSkipName(subNode.getStr("nodeName"));
                                skip.setSkipType(SkipType.PASS.getKey());
                                skip.setNextNodeCode(lastNodeCode);
                                skipList.add(skip);
                            }
                        }
                        // 指定跳转节点
                    }


                }
                node.setSkipList(skipList);
            }
            // 网关 和 分支 都处理了， 这里就是else 流程
            //最后一个节点时 无法再跳转了，  skipNodeCode == null;
            else  if (skipNodeCode != null) {
                FlowSkip skip = new FlowSkip();
                skip.setNodeId(node.getId());
                skip.setNowNodeCode(node.getNodeCode());
                skip.setNextNodeCode(skipNodeCode);
                skip.setSkipType(SkipType.PASS.getKey());
                skipList.add(skip);


                if(!NodeType.isStart(node.getNodeType())){
                    // 每个节点 添加reject ，表示回绝，直接结束， 后面在考虑 回退和 拒绝 和 驳回 的操作
                    // 可以加一个互斥网关， 看在哪里好做吧
                    FlowSkip flowSkip = new FlowSkip();
                    flowSkip.setSkipType(SkipType.REJECT.getKey());
                    flowSkip.setNodeId(node.getId());
                    flowSkip.setNowNodeCode(node.getNodeCode());
                    flowSkip.setNextNodeCode(endNodeCode);
                    skipList.add(flowSkip);

                }

                 node.setSkipList(skipList);




            }
            // 指定下一个跳转节点
            if(isParallel){
                List<Skip> parallelSkipList = new ArrayList<>();
                for(String parallelSkipNodecode : parallelFirstNodes){
                    Skip skip = new FlowSkip();
                    skip.setNowNodeCode(parallelCopy.getNodeCode());
                    skip.setSkipType(SkipType.PASS.getKey());
                    skip.setNextNodeCode(parallelSkipNodecode);
                    parallelSkipList.add(skip);
                }
                parallelCopy.setSkipList(parallelSkipList);
                nodeList.add(parallelCopy);
                skipNodeCode = parallelCopy.getNodeCode();
            }else{
                skipNodeCode = node.getNodeCode();
            }
            // 处理完成后 再 处理当前节点


            nodeList.add(node);



        }
        return skipNodeCode;
    }


    private static SeaFlowNode convertToNode(JSONObject jsonObject) {
        SeaFlowNode node1 = new SeaFlowNode();
        String nodeType = jsonObject.getStr("nodeType");
        // 转换  parallel-node ， 因为格式不同， 这里需要做个转换
        if(ObjectUtil.equal(nodeType, "parallel-node")){
            nodeType = "between";
        }
        Integer keyByValue = NodeType.getKeyByValue(nodeType);
        // nodeId 作为code
        String nodeCode = jsonObject.getStr("nodeId");
        String nodeName = jsonObject.getStr("nodeName");
        String value = jsonObject.getStr("value");
        String SkipAnyNode = jsonObject.getStr("SkipAnyNode");

//        BigDecimal nodeRatio = jsonObject.getBigDecimal("nodeRatio");
//        String permissionFlag = jsonObject.getStr("permissionFlag");

        //权限相关
        JSONObject config = jsonObject.getJSONObject("config");
        if(ObjectUtil.isNotEmpty(config)){
            BigDecimal nodeRatio = config.getBigDecimal("nodeRatio");
            //
            String permission = convertPermissionMap(config);
//            String permission = config.getStr("permission");
            node1.setNodeRatio(nodeRatio);
            node1.setPermissionFlag(permission);
            // 多个用逗号分割， 这里先写死
            node1.setListenerType(String.join(",", Listener.LISTENER_ASSIGNMENT, Listener.LISTENER_CREATE));
            node1.setListenerPath(String.join("@@", GlobalAssignmentListener.class.getName(),
                    GlobalCreateListener.class.getName()));
            // 审批为空配置信息
            String emptyApprove = config.getStr("emptyApprove");
            node1.setEmptyApprove(emptyApprove);
        }


        node1.setNodeType(keyByValue);
        node1.setNodeCode(nodeCode);

        node1.setNodeName(nodeName);
        node1.setSkipAnyNode(SkipAnyNode);
        return node1;
    }

    private static String convertPermissionMap(JSONObject config) {
        String roleCode= "role";
        String userCode= "user";
        String permission = "";
        // 设置权限  role, user 等
        JSONArray permissions = config.getJSONArray("permissions");
        // 权限组合
        JSONObject combination = config.getJSONObject("combination");
        if(ObjectUtil.isNull(permissions)){
            return null;
        }
        List<String> list = permissions.stream().map(String::valueOf).collect(Collectors.toList());//.toList();
        if(ObjectUtil.isEmpty(list)){
            return null;
        }
        if(list.contains(roleCode)){
            JSONArray jsonArray = combination.getJSONArray(roleCode);
            List<String> roleIds = jsonArray.stream().map(r -> ((JSONObject) r).getStr("id")).collect(Collectors.toList());//.toList();
            if(ObjectUtil.isNotEmpty(roleIds)){
                String collect = roleIds.stream().collect(Collectors.joining(","));
                permission += "role:" + collect + "@@";
            }
        }

        if(list.contains(userCode)){
            JSONArray jsonArray = combination.getJSONArray(userCode);
            List<String> roleIds = jsonArray.stream().map(r -> ((JSONObject) r).getStr("id")).collect(Collectors.toList());//.toList();
            if(ObjectUtil.isNotEmpty(roleIds)){
                String collect = roleIds.stream().collect(Collectors.joining(","));
                permission += "user:" + collect + "@@";
            }
        }
        if(!permission.isEmpty()) {
            permission = permission.substring(0, permission.length() - 2);
        }
        return permission;
    }


}

